﻿using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using LFSRecord2.Events;
using LFSRecord2.Misc;
using LFSRecord2.Model;
using LFSRecord2.Model.Media;
using Microsoft.Win32;
using System.Diagnostics;
using LFSRecord2.ViewModel;

namespace LFSRecord2.View.MediaStructures
{
    /// <summary>
    /// Interaction logic for MediaObjectView.xaml
    /// </summary>
    public partial class MediaObjectView : UserControl
    {
        public static readonly DependencyProperty FileStatusProperty = DependencyProperty.Register(
            "FileStatus",
            typeof(MediaFileStatus),
            typeof(MediaObjectView),
            new FrameworkPropertyMetadata(new PropertyChangedCallback(OnFileStatusChanged))
        );
        public MediaFileStatus FileStatus
        {
            get { return (MediaFileStatus)GetValue(FileStatusProperty); }
            set { SetValue(FileStatusProperty, value); }
        }
        private static void OnFileStatusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((MediaObjectView)d).DrawMediaInfoGrid();
        }

        public bool IsDragging { get; set; }

        public MediaObjectView()
        {
            InitializeComponent();
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            SetupContextMenu();

            MediaBase mb = (MediaBase)DataContext;

            Binding myBinding = new Binding("FileStatus");
            myBinding.Mode = BindingMode.OneWay;
            myBinding.Source = mb;
            SetBinding(MediaObjectView.FileStatusProperty, myBinding);

            DrawMediaInfoGrid();
            PreviewMouseMove += new MouseEventHandler(MediaObjectView_PreviewMouseMove);
        }

        void DrawMediaInfoGrid()
        {
            MediaInfoGrid.Children.Clear();

            // Populate MediaInfoGrid in case of error
            TextBlock tb;
            MediaBase mb = (MediaBase)DataContext;
            if (FileStatus != MediaFileStatus.Loaded)
            {
                MediaNameGrid.Background = Brushes.Red;
                tb = new TextBlock();
                tb.FontSize = 9;
                tb.Margin = new Thickness(2, 0, 2, 0);
                switch (FileStatus)
                {
                    case MediaFileStatus.None:
                        tb.Text = "No source";
                        break;
                    case MediaFileStatus.NotFound:
                        tb.Text = "File Not Found";
                        break;
                    case MediaFileStatus.NotLoaded:
                        tb.Text = "Error";
                        tb.ToolTip = mb.FileErrorString;
                        break;
                }
                MediaInfoGrid.Children.Add(tb);
                return;
            }

            // Populate MediaInfoGrid with content preview
            MediaNameGrid.Background = new SolidColorBrush(Color.FromRgb(85, 85, 85));
            switch (mb.MediaType)
            {
                case MediaTypes.ReplayData:
                    MediaReplayData mbrd = (MediaReplayData)mb;
                    tb = new TextBlock();
                    tb.FontSize = 9;
                    tb.Margin = new Thickness(2, 0, 2, 0);

                    tb.Text = "Start : " + TimeHelper.Seconds2TimeStamp(mbrd.RData.RecordStartTime) +
                        "\nEnd : " + TimeHelper.Seconds2TimeStamp(mbrd.RData.RecordEndTime) +
                        "\nNum ply : " + mbrd.RData.Players.Count;

                    MediaInfoGrid.Children.Add(tb);
                    break;
                case MediaTypes.Audio:
                    MediaAudio ma = (MediaAudio)mb;
                    tb = new TextBlock();
                    tb.FontSize = 9;
                    tb.Margin = new Thickness(2, 0, 2, 0);

                    tb.Text = ma.AudioStream.AudioType.ToString().ToUpperInvariant() + " - " + (ma.AudioStream.Format.nChannels == 1 ? "Mono" : "Stereo") +
                        "\n" + ma.AudioStream.Format.nSamplesPerSec + "Hz, " + ma.AudioStream.Format.wBitsPerSample + " bits" +
                        "\n" + TimeHelper.Seconds2TimeStamp(ma.AudioStream.Duration);

                    MediaInfoGrid.Children.Add(tb);
                    break;
                case MediaTypes.Bitmap:
                    break;
                case MediaTypes.Video:
                    break;
            }
        }

        Point _startPoint;
        private void Media_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;

            MediaBase mb = (MediaBase)DataContext;
            if (!mb.IsSelected)
            {
                if (!Keyboard.IsKeyDown(Key.LeftShift))
                    LfsRecordController.project.DeselectAllMedia();
                mb.IsSelected = true;
            }

            if (Mouse.LeftButton == MouseButtonState.Pressed)
            {
                _startPoint = e.GetPosition(null);
            }
        }

        private void Media_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;

            MediaBase mb = (MediaBase)DataContext;
            if (!mb.IsSelected)
            {
                if (!Keyboard.IsKeyDown(Key.LeftShift))
                    LfsRecordController.project.DeselectAllMedia();
                mb.IsSelected = true;
            }
        }

        void MediaObjectView_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed && !IsDragging)
            {
                Point position = e.GetPosition(null);

                if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
                    Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
                {
                    StartDrag(e);
                }
            }
        }

        private void StartDrag(MouseEventArgs e)
        {
            if (MediaRenameBox.Visibility == Visibility.Visible)
                return;

            Debug.WriteLine("Starting drag : " + DataFormats.Text.ToString());
            
            MediaBase mb = (MediaBase)DataContext;
            DataObject data;
            if (mb.MediaType == MediaTypes.Audio)
                data = new DataObject(DataFormats.Text.ToString(), "MediaAudio|" + mb.FilePath);
            else if (mb.MediaType == MediaTypes.ReplayData)
                data = new DataObject(DataFormats.Text.ToString(), "MediaReplayData|" + mb.FilePath);
            else
                return;

            IsDragging = true;
            DragDropEffects de = DragDrop.DoDragDrop(this, data, DragDropEffects.Move);
            IsDragging = false;
        }

        public void SetupContextMenu()
        {
            // Create context menu
            MenuItem mi, mi2;
            ContextMenu cm = new ContextMenu();
            //MediaBase mb = (MediaBase)DataContext;

            mi = new MenuItem();
            mi.Name = "Rename";
            mi.Header = "Rename";
            mi.Command = RenameMediaCommand;
            cm.Items.Add(mi);

            mi = new MenuItem();
            mi.Name = "LocateFile";
            mi.Header = "Locate File";
            mi.Command = LocateFileCommand;
            cm.Items.Add(mi);

            mi = new MenuItem();
            mi.Name = "Reload";
            mi.Header = "Reload";
            mi.Command = ReloadMediaCommand;
            cm.Items.Add(mi);

            cm.Items.Add(new Separator());

            mi = new MenuItem();
            mi.Name = "DeleteSelected";
            mi.Header = "Delete Selected";
            mi.Command = DeleteSelectedCommand;
            cm.Items.Add(mi);

            cm.Items.Add(new Separator());

            mi = new MenuItem();
            mi.Name = "Import";
            mi.Header = "Import";

            mi2 = new MenuItem();
            mi2.Name = "ImportMediaFile";
            mi2.Header = "Import Media File";
            mi2.Command = ImportMediaCommand;
            mi.Items.Add(mi2);

            mi2 = new MenuItem();
            mi2.Name = "ImportRDLfs";
            mi2.Header = "Replay Data From LFS";
            mi2.Command = ImportRDFromLFSCommand;
            mi.Items.Add(mi2);
            cm.Items.Add(mi);

            ContextMenu = cm;
        }

        // DeleteSelectedCommand
        RelayCommand _deleteSelectedCommand;
        public ICommand DeleteSelectedCommand
        {
            get
            {
                if (_deleteSelectedCommand == null)
                    _deleteSelectedCommand = new RelayCommand(param => DeleteSelectedCommandExecute());
                return _deleteSelectedCommand;
            }
        }
        static void DeleteSelectedCommandExecute()
        {
            LfsRecordController.project.DeleteSelectedMediaObjects();
            LfsRecordController.project.Edited = true;
        }

        // ReloadMediaCommand
        RelayCommand _reloadMediaCommand;
        public ICommand ReloadMediaCommand
        {
            get
            {
                if (_reloadMediaCommand == null)
                    _reloadMediaCommand = new RelayCommand(param => this.ReloadMediaCommandExecute());
                return _reloadMediaCommand;
            }
        }
        void ReloadMediaCommandExecute()
        {
            ContextMenu.IsOpen = false;

            Mouse.OverrideCursor = Cursors.Wait;
            MediaBase mb = (MediaBase)DataContext;
            LfsRecordController.project.ReloadMediaObject(mb);
            ServicesFactory.EventService.GetEvent<FrameIndexChangedEvent>().Publish(
                new FrameIndexChangedEventArgs()
                {
                    Sender = this,
                    NewFrameIndex = LfsRecordController.FrameIndex,
                    UpdateCurrentValues = true
                });
            Mouse.OverrideCursor = null;
        }

        // RenameMediaCommand
        RelayCommand _renameMediaCommand;
        public ICommand RenameMediaCommand
        {
            get
            {
                if (_renameMediaCommand == null)
                    _renameMediaCommand = new RelayCommand(param => this.RenameMediaCommandExecute());
                return _renameMediaCommand;
            }
        }
        void RenameMediaCommandExecute()
        {
            // Overlay a textbox to write the new file name in.
            MediaNameLabel.Visibility = Visibility.Hidden;

            MediaBase mb = (MediaBase)DataContext;
            MediaRenameBox.Text = Path.GetFileNameWithoutExtension(mb.FilePath);
            MediaRenameBox.Visibility = Visibility.Visible;
            MediaRenameBox.SelectAll();
            MediaRenameBox.Focus();

            MediaRenameBox.KeyDown += MediaRenameBox_KeyDown;
            Application.Current.MainWindow.PreviewMouseDown += Global_MouseDown;
            LfsRecordController.project.Edited = true;
        }

        // LocateFileCommand
        RelayCommand _locateFileCommand;
        public ICommand LocateFileCommand
        {
            get
            {
                if (_locateFileCommand == null)
                    _locateFileCommand = new RelayCommand(param => this.LocateFileCommandExecute());
                return _locateFileCommand;
            }
        }
        void LocateFileCommandExecute()
        {
            MediaBase mb = (MediaBase)DataContext;
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.InitialDirectory = Path.GetDirectoryName(mb.FilePath);
            dlg.FileName = Path.GetFileName(mb.FilePath);
            if (mb.MediaType == MediaTypes.ReplayData)
                dlg.Filter = "LFS Replay Data|*.lfsrd";
            else if (mb.MediaType == MediaTypes.Audio)
                dlg.Filter = "Audio Files|*.wav;*.aif";
            else
                dlg.Filter = "All Files|*.*";
            dlg.CheckFileExists = true;
            dlg.CheckPathExists = true;

            if (dlg.ShowDialog() ?? false)
            {
                //Debug.WriteLine("New full path : " + dlg.FileName);
                Mouse.OverrideCursor = Cursors.Wait;
                string oldFilePath = mb.FilePath;
                mb.FilePath = dlg.FileName;
                mb.ReloadFile();
                LfsRecordController.project.RenameMedia(oldFilePath, mb.FilePath);
                LfsRecordController.project.Edited = true;
                Mouse.OverrideCursor = null;
                LfsRecordController.project.Edited = true;
            }
        }

        // ImportMediaFileCommand
        RelayCommand _importMediaCommand;
        ICommand ImportMediaCommand
        {
            get
            {
                if (_importMediaCommand == null)
                    _importMediaCommand = new RelayCommand(param => ImportMediaCommandExecute());
                return _importMediaCommand;
            }
        }
        static void ImportMediaCommandExecute()
        {
            MainWindowViewModel.ShowImportMediaDialog();
        }

        // ImportRDFromLFSCommand
        RelayCommand _importRDFromLFSCommand;
        ICommand ImportRDFromLFSCommand
        {
            get
            {
                if (_importRDFromLFSCommand == null)
                    _importRDFromLFSCommand = new RelayCommand(param => ImportRDFromLFSCommandExecute(), param => ImportRDFromLFSCommandCanExecute());
                return _importRDFromLFSCommand;
            }
        }
        static void ImportRDFromLFSCommandExecute()
        {
            MainWindowViewModel.ShowImportReplayDataFromLFSDialog();
        }
        static bool ImportRDFromLFSCommandCanExecute()
        {
            return (LfsRecordController.project.HasReplay() && LfsRecordController.insim != null && LfsRecordController.insim.IsConnected);
        }
        
        private void MediaRenameBox_KeyDown(object sender, KeyEventArgs e)
        {
            //Debug.WriteLine("kb input : " + e.Key);
            MediaBase mb = (MediaBase)DataContext;
            if (e.Key == Key.Enter)
            {
                e.Handled = true;
                MediaRenameBox.Text = MediaRenameBox.Text.Trim();
                if (MediaRenameBox.Text.Length == 0 || MediaRenameBox.Text.Length > 60)
                    return;

                string dir = Path.GetDirectoryName(mb.FilePath);
                string ext = Path.GetExtension(mb.FilePath);
                
                try
                {
                    // Remove existing extension, because we'll add it again after this.
                    if (Path.GetExtension(MediaRenameBox.Text) == ext)
                        MediaRenameBox.Text = Path.GetFileNameWithoutExtension(MediaRenameBox.Text);

                    string oldFilePath = mb.FilePath;
                    mb.RenameFile(dir + "\\" + MediaRenameBox.Text + "" + ext);
                    if (oldFilePath != mb.FilePath)
                        LfsRecordController.project.RenameMedia(oldFilePath, mb.FilePath);
                    LfsRecordController.project.Edited = true;
                    ServicesFactory.EventService.GetEvent<FrameIndexChangedEvent>().Publish(
                        new FrameIndexChangedEventArgs() 
                        { 
                            Sender = this, 
                            NewFrameIndex = LfsRecordController.FrameIndex, 
                            UpdateCurrentValues = true });
                }
                catch (Exception ex)
                {
                    MessageBox.Show(
                        App.Current.MainWindow,
                        ex.Message,
                        "Rename Error",
                        MessageBoxButton.OK,
                        MessageBoxImage.Error);
                }
                
                HideInputBox();
            }
            else if (e.Key == Key.Tab)
            {
                e.Handled = true;
            }
            else if (e.Key == Key.Escape)
            {
                e.Handled = true;
                HideInputBox();
            }
        }

        private void HideInputBox()
        {
            HideInputBox(true);
        }
        private void HideInputBox(bool focusTurnAway)
        {
            Application.Current.MainWindow.PreviewMouseDown -= Global_MouseDown;
            MediaRenameBox.KeyDown -= MediaRenameBox_KeyDown;
            MediaRenameBox.Visibility = Visibility.Collapsed;
            if (focusTurnAway)
                Application.Current.MainWindow.Focus();
            MediaNameLabel.Visibility = Visibility.Visible;
        }

        private void Global_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Debug.WriteLine("Sender type : " + e.OriginalSource.GetType());
            MediaBase mb = (MediaBase)DataContext;
            if (e.OriginalSource.GetType().ToString() != "System.Windows.Controls.TextBoxView")
            {
                Debug.WriteLine("Sender type check : " + e.OriginalSource.GetType());
                MediaRenameBox.Text = MediaRenameBox.Text.Trim();
                if (MediaRenameBox.Text.Length > 0 && MediaRenameBox.Text.Length < 60)
                {
                    string dir = Path.GetDirectoryName(mb.FilePath);
                    string ext = Path.GetExtension(mb.FilePath);

                    // Remove existing extension, because we'll add it again after this.
                    if (Path.GetExtension(MediaRenameBox.Text) == ext)
                        MediaRenameBox.Text = Path.GetFileNameWithoutExtension(MediaRenameBox.Text);

                    try
                    {
                        string oldFilePath = mb.FilePath;
                        mb.RenameFile(dir + "\\" + MediaRenameBox.Text + "" + ext);
                        if (oldFilePath != mb.FilePath)
                            LfsRecordController.project.RenameMedia(oldFilePath, mb.FilePath);
                        LfsRecordController.project.Edited = true;
                        ServicesFactory.EventService.GetEvent<FrameIndexChangedEvent>().Publish(
                            new FrameIndexChangedEventArgs()
                            {
                                Sender = this,
                                NewFrameIndex = LfsRecordController.FrameIndex,
                                UpdateCurrentValues = true
                            });
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(
                            App.Current.MainWindow,
                            ex.Message,
                            "Rename Error",
                            MessageBoxButton.OK,
                            MessageBoxImage.Error);
                    }
                }
                HideInputBox();
            }
        }
    }
}
